package com.maple.retry.retrytask.config;

import com.alibaba.fastjson.JSON;
import com.maple.retry.retrytask.RetryTaskExecutor;
import com.maple.retry.retrytask.ScheduleStrategy;
import com.maple.retry.retrytask.manager.RetryTaskManager;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * @author: maple
 * @version: CompensateTaskConfig.java, v 0.1 2020年12月02日 10:06 maple Exp $
 */
@Slf4j
public final class RetryTaskConfig implements InitializingBean {

    /**
     * Runtime Param Prefix
     */
    private static final String RUNTIME_PARAM_PREFIX = "$";

    /**
     * Runtime Param Default Value
     */
    private static final String RUNTIME_PARAM_DEFAULT = "default";

    /**
     * 任务执行器
     */
    private RetryTaskExecutor executor;

    /**
     * 结果决策器
     */
    private ScheduleStrategy scheduleStrategy;

    /**
     * 补偿任务默认查询数量
     */
    private String loadSize = "200";

    /**
     * 补偿任务首次调度延迟时间, 毫秒值
     */
    private long delay = 0L;

    /**
     * 补偿任务类型
     */
    private String bizType;

    /**
     * retry intervals
     */
    private String retryIntervals;

    @Autowired
    private RetryTaskManager retryTaskManager;

    @Override
    public void afterPropertiesSet() throws Exception {

        fetchConfig();

        initScheduleStrategy();

        retryTaskManager.register(bizType, this);

        log.info("register a new compensate task, bizType=" + this.bizType);
    }

    private void initScheduleStrategy() {
        if (this.scheduleStrategy == null) {
            this.scheduleStrategy = new ScheduleStrategy(this.retryIntervals);
            return;
        }

        this.scheduleStrategy.reset(this.retryIntervals);
    }

    private void fetchConfig() {

        // retry intervals config
        if (paramWithPrefix(this.retryIntervals)) {
            fetchRetryIntervalsConfig();
        }

        // loaded size config
        if (paramWithPrefix(this.loadSize)) {
            fetchLoadSizeConfig();
        }
    }

    private void fetchLoadSizeConfig() {

        Map<String, String> map = new HashMap<>();

        // 从配置文件里或者DB里取
        String value = "{\"default\":\"20\",\"sync.kyc.result.load\":\"20\",\"sync.kyc.info.load\":\"20\",\"aml.sync.check.load\" :\"60\", \"sync.aml.result.load\":\"20\"}";
        map.put("retryTask.loaderSize", value);

        Map<String, String> loadSizeMap = JSON.parseObject(map.get("retryTask.loaderSize"), Map.class);

        if (null != loadSizeMap && !loadSizeMap.isEmpty()) {

            String key = StringUtils.remove(this.loadSize, RUNTIME_PARAM_PREFIX);

            this.loadSize = Optional.ofNullable(loadSizeMap.get(key)).orElse(loadSizeMap.get(RUNTIME_PARAM_DEFAULT));
        }
    }

    private boolean paramWithPrefix(String value) {
        return StringUtils.startsWith(value, "$");
    }

    private void fetchRetryIntervalsConfig() {

        Map<String, String> map = new HashMap<>();

        // 从配置文件里或者DB里取
        String value = "{\"default\":\"time:1m,1m,1m,2m,2m,5m,10m,1h,12h,24h\",\"sync.kyc.result.retry\":\"time:1m,2m,5m,10m,1h,2h,6h,12h,12h,24h\",\"sync.kyc.info.retry\":\"time:1m,2m,5m,10m,1h,2h,6h,12h,12h,24h\",\"aml.sync.check.retry\":\"time:1m,2m,5m,10m,1h,2h,6h,12h,12h,24h\",\"sync.aml.result.retry\":\"time:1m,2m,5m,10m,1h,2h,6h,12h,12h,24h\"}";
        map.put("retryTask.retryIntervals", value);

        Map<String, String> retryIntervalsMap = JSON.parseObject(map.get("retryTask.retryIntervals"), Map.class);

        if (null != retryIntervalsMap && !retryIntervalsMap.isEmpty()) {

            String key = StringUtils.remove(this.retryIntervals, RUNTIME_PARAM_PREFIX);

            this.retryIntervals = Optional.ofNullable(retryIntervalsMap.get(key)).orElse(retryIntervalsMap.get(RUNTIME_PARAM_DEFAULT));
        }
    }

    /**
     * 对配置合法性校验
     */
    public void validation() {
        Assert.notNull(this.executor, "CompensateTaskConfig.compensator不能为null");
        Assert.notNull(this.getScheduleStrategy(), "CompensateTaskConfig.policyVoter不能为null");
        Assert.isTrue(StringUtils.isNumeric(this.getLoadSize()), "CompensateTaskConfig.loadSize must be numeric");
        Assert.isTrue(Integer.valueOf(this.getLoadSize()) > 0, "CompensateTaskConfig.loadSize不能小于0");
        Assert.hasText(this.getScheduleStrategy().getStrRetryIntervals(), "重试配置不能为空");
        Assert.isTrue(this.getScheduleStrategy().getLongRetryIntervals().length > 0, "重试配置非法,检查是否满足\"time:[Number][Unit],[Number][Unit]...[Number][Unit]\"格式");
    }

    public String getLoadSize() {
        return loadSize;
    }

    public void setLoadSize(String loadSize) {
        this.loadSize = loadSize;
    }

    /**
     * 返回延迟时间
     *
     * @return 延迟时间，单位为毫秒
     */
    public long getDelayInMills() {
        return delay;
    }

    public void setBizType(String bizType) {
        this.bizType = bizType;
    }

    public String getRetryIntervals() {
        return retryIntervals;
    }

    public void setRetryIntervals(String retryIntervals) {
        this.retryIntervals = retryIntervals;
    }

    public RetryTaskExecutor getExecutor() {
        return executor;
    }

    public void setExecutor(RetryTaskExecutor executor) {
        this.executor = executor;
    }

    public ScheduleStrategy getScheduleStrategy() {
        return scheduleStrategy;
    }

    /**
     * Setter method for property scheduleStrategy.
     *
     * @param scheduleStrategy value to be assigned to property scheduleStrategy
     */
    public void setScheduleStrategy(ScheduleStrategy scheduleStrategy) {
        this.scheduleStrategy = scheduleStrategy;
    }
}
